/*
 * TP 1 - IA Miage : Sudoku
 * 
 * @author Solen Quiniou.
 */
import java.util.*;

public class Grille {
	private Case[][] _cases;
	private int _taille;
	private int _nbCasesVides;
	
	
	public Grille(int n){
		_taille = n;
		_nbCasesVides = n*n;
		_cases = new Case[n][n];
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				_cases[i][j] = new Case(i, j, n);
			}
		}
	}
	
	public Grille(int n, int[][] grille){
		_taille = n;
		_nbCasesVides = n*n;
		_cases = new Case[n][n];
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				_cases[i][j] = new Case(i, j, grille[i][j], n);
				if(_cases[i][j].getVal() != 0){
					_nbCasesVides --;
				}
			}
		}
	}
	
	public Grille(Grille grille){
		_taille = grille._taille;
		_nbCasesVides = grille._nbCasesVides;
		_cases = new Case[_taille][_taille];
		for(int i=0; i<_taille; i++){
			for(int j=0; j<_taille; j++){
				_cases[i][j] = new Case(grille.getCase(i, j));
			}
		}
	}
	
	
	public Case getCase(int i, int j){
		return _cases[i][j];
	}
	
	public int getNbCasesVides(){
		return _nbCasesVides;
	}
	
	public void initRestant(){
		for(int i=0; i<_taille; i++){
			for(int j=0; j<_taille; j++){
				// HACK: Pas tiptop de l'initialiser les choix restant après la création de la case.
				Set<Integer> restant = choixRestant(_cases[i][j]);
				_cases[i][j].setRestant(restant);
			}
		}
	}
	
	/*
	 * Fonction qui donne une case sans valeur, s'il en existe une.
	 */
	public Case getCasePossible(){
		Case casePoss = null;
		int min = 9;
		int i = 0;
		int j = 0;
		while(i<9) {
			while(j<9){
				if(_cases[i][j].getVal() == 0) {					// Si la case est vide,
					if(min >= choixRestant(_cases[i][j]).size()){	// On regarde si elle a moins de possibilite
						casePoss = _cases[i][j];					// Si oui, on prend cette case la
						min = choixRestant(casePoss).size();		// Et on change la valeur mini
					}
				}
				j++;
			}
			j=0;
			i++;
		}
		return casePoss;
	}
	
	/*
	 * Fonction qui calcul le nombre de choix possible pour la case c
	 */
	public Set<Integer> choixRestant(Case c){
		Set<Integer> resultat= new HashSet<Integer>();
		// On regarde pour chaque valeur si elle est possible
		for(int v=1;v<=9;v++){
			// Est-ce que ça passe pour la ligne?
			if(valideLigne(v,c.getI())){
			   // Est-ce que ça passe pour la colonne?
			   if(valideColonne(v,c.getJ())){
				   // Est-ce que ça passe pour le bloc?
				   if(valideBloc(v,c)){
					   // C'est bon c'est valide pour v
					   resultat.add(v);
				   }
			   }
			}
		}
		return resultat;
	}
	
	/*
	 * Fonction qui calcul si la valeur v est valide sur la ligne l
	 */
	public boolean valideLigne(int v,int l){
		boolean resultat = true;
		// HACK: Faire un while pour arrete des que false
		for(int i=0;i<9;i++){
			if(_cases[l][i].getVal()==v){resultat=false;}
		}
		return resultat;
	}
	
	/*
	 * Fonction qui calcul si la valeur v est valide sur la colonne c
	 */
	public boolean valideColonne(int v,int c){
		boolean resultat = true;
		// HACK: Faire un while pour arrete des que false
		for(int i=0;i<9;i++){
			if(_cases[i][c].getVal()==v){resultat=false;}
		}
		return resultat;
	}
	
	/*
	 * Fonction qui calcul si la valeur v est valide sur le bloc de la case c
	 */
	public boolean valideBloc(int v,Case c){
		boolean resultat = true;
		// On recupere le debut et fin du bloc
		int i = 3*(c.getI()/3);
		int j = 3*(c.getJ()/3);
		int ifin = i+2;
		int jfin = j+2;
		// HACK: Faire un while pour arrete des que false
		while(i<=ifin) {
			j=3*(c.getJ()/3);
			while(j<=jfin) {
				if(_cases[i][j].getVal()==v) {resultat=false;}
				j++;
			}
			i++;
		}
		return resultat;
	}
	
	
	/*
	 * Fonction qui donne la valeur v ‡ la case c.
	 */
	public void setCase(Case c, int v){
		// On commence par verifie si l'on va devoir augmenter ou diminuer le nombre de case vide
		if(c.getVal()==0 & v!=0){		// On diminue si la case etait vide et que l'on met une valeur dedans
			_nbCasesVides--;
		}
		else if(c.getVal()!=0 & v==0){	// On augmente si la case est pleine et qu'on la vide
			_nbCasesVides++;
		}
		_cases[c.getI()][c.getJ()].setVal(v);
	}
	
	
	/*
	 * Fonction qui rend vrai si la valeur v peut Ítre donnÈe ‡ la case C
	 * c'est-‡-dire si la grille respecte toujours les contraintes du Sudoku.
	 */
	public boolean casePossible(Case c, int v){
		return valideLigne(v,c.getI()) && valideColonne(v,c.getJ()) && valideBloc(v,c);
	}
	
	
	public void afficheGrille(){
		int v;
		int dim = (int)Math.sqrt(_taille);
		for(int i=0; i<_taille; i++){
			if(i%dim == 0){
				System.out.print(" ");
				for(int k=0; k<=_taille; k++)
					System.out.print("--");
				System.out.println();
			}
			for(int j=0; j<_taille; j++){
				if(j%dim == 0){
					System.out.print("|");
				}
				v = _cases[i][j].getVal();
				if(v == 0){
					System.out.print("  ");					
				}
				else{
					System.out.print(v + " ");
				}
			}
			System.out.println("|");
		}
		System.out.print(" ");
		for(int k=0; k<=_taille; k++)
			System.out.print("--");
		System.out.println();		
	}
}
